#include"DesignExtractor.h"

using namespace std;

BOOLEAN DesignExtractor::fillTable() {
	VAR_INDEX lhs;
	PROC_INDEX tempProc;
	STATEMENT_INDEX tempStmt;
	vector<STATEMENT_INDEX> callStmt;
	vector<VAR_INDEX> tempModifyProc;
	vector<VAR_INDEX> tempUseProc;
	astquery->gotoStmtAt(astquery->numStmts());
	tempProc = astquery->getCurrProcID();
	tempStmt = astquery->getCurrStmtIndex();
	procTable->insertProc(astquery->getCurrProcID(),astquery->getCurrStmtIndex(),RANGE_END);
	do
	{
		STMT_VCTR stmtVector;
		getNextStmts(stmtVector);
		statTable->addEntry(astquery->getCurrStmtIndex(),astquery->getCurrStmtType(),astquery->getLinksId()[stmtNode::FOLLOWS],astquery->getLinksId()[stmtNode::PARENT],stmtVector);
		tempStmt = astquery->getCurrStmtIndex();
		if(astquery->getCurrProcID() != tempProc) {
			procTable->insertProc(tempProc,astquery->getCurrStmtIndex()+1,RANGE_START);
			usesProc->addEntry(tempUseProc);
			modifyProc->addEntry(tempModifyProc);
			int tempo = tempUseProc.size();
			for(int i =0;i < tempo;i++) {
				tempUseProc.pop_back();
			}
			tempo = tempModifyProc.size();
			for(int i =0;i < tempo; i++) {
				tempModifyProc.pop_back();
			}
			tempProc = astquery->getCurrProcID();
			procTable->insertProc(astquery->getCurrProcID(),astquery->getCurrStmtIndex(),RANGE_END);
		}
		int temp = RHS_LIST.size();
		for(int k =0;k<temp;k++) {
			RHS_LIST.pop_back();
		}
		if(astquery->getCurrStmtType() == STATEMENT_Assign) {
			lhs = ((numeric_expr*)astquery->getLHS())->getVARIndex();
			vector<VAR_INDEX> lhsList;
			lhsList.push_back(lhs);
			expr* e =astquery->getRHS();
			parseExpr(NULL,e);
			modifyStat->addEntry(lhsList);
			usesStat->addEntry(RHS_LIST);
			for(int j =0; j<RHS_LIST.size();j++) {
				bool temp1 = false;
				for(int i =0; i<tempUseProc.size();i++) {
					if(tempUseProc.at(i) == RHS_LIST.at(j)) {
						temp1 = true;
					}
				}
				if(temp1 == false) {
					tempUseProc.push_back(RHS_LIST.at(j));
				}
			}
			for(int j =0; j<lhsList.size();j++) {
				bool temp2 = false;
				for(int i =0; i<tempModifyProc.size();i++) {
					if(tempModifyProc.at(i) == lhsList.at(j)) {
						temp2 = true;
					}
				}
				if(temp2 == false) {
					tempModifyProc.push_back(lhsList.at(j));
				}
			}
			modifyVar->addStmt(lhs,astquery->getCurrStmtIndex());
			modifyVar->addProc(lhs,astquery->getCurrProcID());
			for(int i=0;i<RHS_LIST.size();i++) {
				usesVar->addStmt(RHS_LIST.at(i),astquery->getCurrStmtIndex());
				usesVar->addProc(RHS_LIST.at(i),astquery->getCurrProcID());
			}

		}
		else if(astquery->getCurrStmtType() == STATEMENT_While) {
			vector<VAR_INDEX> usedVar;
			vector<VAR_INDEX> modifiedVar;
			VAR_INDEX conditionVar = astquery->getConditionVar();
			usedVar.push_back(conditionVar);
			bool temp2 = false;
			for(int i =0; i<tempUseProc.size();i++) {
				if(tempUseProc.at(i) == conditionVar) {
					temp2 = true;
				}
			}
			if(temp2 == false) {
				tempUseProc.push_back(conditionVar);
			}
			getModifyUsesList(astquery->numStmts()-astquery->getCurrStmtIndex()+1,usedVar,modifiedVar,BLOCK_WHILE);
			modifyStat->addEntry(modifiedVar);
			usesStat->addEntry(usedVar);
			for(int i=0;i<modifiedVar.size();i++) {
				modifyVar->addStmt(modifiedVar.at(i),astquery->getCurrStmtIndex());
				modifyVar->addProc(modifiedVar.at(i),astquery->getCurrProcID());
			}
			for(int i=0;i<usedVar.size();i++) {
				usesVar->addStmt(usedVar.at(i),astquery->getCurrStmtIndex());
				usesVar->addProc(usedVar.at(i),astquery->getCurrProcID());
			}
		}
		else if(astquery->getCurrStmtType() == STATEMENT_If) {
			vector<VAR_INDEX> usedVar;
			vector<VAR_INDEX> modifiedVar;
			VAR_INDEX conditionVar = astquery->getConditionVar();
			usedVar.push_back(conditionVar);
			bool temp2 = false;
			for(int i =0; i<tempUseProc.size();i++) {
				if(tempUseProc.at(i) == conditionVar) {
					temp2 = true;
				}
			}
			if(temp2 == false) {
				tempUseProc.push_back(conditionVar);
			}
			getModifyUsesList(astquery->numStmts()-astquery->getCurrStmtIndex()+1,usedVar,modifiedVar,BLOCK_IF);
			modifyStat->addEntry(modifiedVar);
			usesStat->addEntry(usedVar);
 			for(int i=0;i<modifiedVar.size();i++) {
				modifyVar->addStmt(modifiedVar.at(i),astquery->getCurrStmtIndex());
				modifyVar->addProc(modifiedVar.at(i),astquery->getCurrProcID());
			}
			for(int i=0;i<usedVar.size();i++) {
				usesVar->addStmt(usedVar.at(i),astquery->getCurrStmtIndex());
				usesVar->addProc(usedVar.at(i),astquery->getCurrProcID());
			}
		}
		else if(astquery->getCurrStmtType() == STATEMENT_Call) {
			vector<VAR_INDEX> temp;
			modifyStat->addEntry(temp);
			usesStat->addEntry(temp);
			callsTable->insertCall(astquery->getCurrProcID(),astquery->getCallProcID());
			callStmt.push_back(astquery->getCurrStmtIndex());

		}
	}while(astquery->gotoPrevLine() == STATUS_Success);
	procTable->insertProc(tempProc,tempStmt,RANGE_START);
	usesProc->addEntry(tempUseProc);
	modifyProc->addEntry(tempModifyProc);
	modifyProc->initialise();
	usesProc->initialise();
	modifyStat->initialise();
	usesStat->initialise();
	statTable->initialise();
	procTable->initialise();
	insertCallsData(callStmt);
	return true;
}

//This function parses the Expression and gets the variables 
//on the RHS of a assign statement without the operaters.
void DesignExtractor::parseExpr(stack<STRING> *s, expr *e) {
	if( s == NULL )
		s = new stack<STRING>();
	expr *currExpr = e;
	if( currExpr->toString() == "+" 
		|| currExpr->toString() == "-"
		|| currExpr->toString() == "*" ) {
			s->push( currExpr->toString() );
			parseExpr(s, ((composite_expr*)currExpr)->getLHS());
			parseExpr(s, ((composite_expr*)currExpr)->getRHS());
	}
	else {
		VAR_INDEX temp = ((numeric_expr*)currExpr)->getVARIndex();
		if(varTable->getVarType(temp) == NUMERIC_EXPR_TYPE_VARIABLE) {
			RHS_LIST.push_back(temp);
		}
	}
}

void DesignExtractor::getModifyUsesList(STATEMENT_INDEX blockNo, vector<VAR_INDEX> &usedVar, vector<VAR_INDEX> &modifiedVar,BLOCK_TYPE type) {
	vector<STATEMENT_INDEX> stmts;
	//Case1: Where list is required for a while statement
	//In this case, the while goes on till getBlockEnd is
	//going to return a -1 or the parent of that statement 
	//is less than the parent of the while block 
	if(type == BLOCK_WHILE) {
		int i = blockNo - 1;
		int temper = astquery->getCurrStmtIndex();
		while(statTable->getParent(i) >= temper) {
			stmts.push_back(i);
			i = statTable->getBlockEnd(i);
			if(i == -1)
				break;
			i = astquery->numStmts()-i+1;
		}
	}
	//Case2: Where list is required for a if statement
	//In this case, the if goes on till getBlockEnd is
	//going to return a -1 for the then block and then 
	//run again till it encounters another -1 for the 
	//else bloc or till the parent of that statement 
	//is less than the parent of the while block 
	else if (type == BLOCK_IF) {
		int counter = 0;
		int i = blockNo - 1;
		int temper = astquery->getCurrStmtIndex();
		while(statTable->getParent(i) >= temper) {
			stmts.push_back(i);
			if(statTable->getBlockEnd(i) == -1) {
				if(counter == 0) {
					i--;
					counter++;
				}
				else {
					break;
				}
			}
			else {
				i = statTable->getBlockEnd(i);
				i = astquery->numStmts()-i+1;
			}
		}
	}
	//This loop adds the varaibles used/modified in the statement list
	//to the reference variables to add to the while/if block
	for(int k=0;k<stmts.size();k++) {
		vector<VAR_INDEX> tempMod = modifyStat->getVarModified(stmts.at(k));
		vector<VAR_INDEX> tempUse = usesStat->getVarUsed(stmts.at(k));
		for(int i=0;i<tempMod.size();i++) {
			bool temp1 = true;
			for(int j=0;j<modifiedVar.size();j++) {
				if(tempMod.at(i) == modifiedVar.at(j)) {
					temp1 = false;
					break;
				}
			}
			if(temp1 == true) {
				modifiedVar.push_back(tempMod.at(i));
			}
		}
		for(int i=0;i<tempUse.size();i++) {
			bool temp2 = true;
			for(int j=0;j<usedVar.size();j++) {
				if(tempUse.at(i) == usedVar.at(j)) {
					temp2 = false;
					break;
				}
			}
			if(temp2 == true) {
				usedVar.push_back(tempUse.at(i));
			}
		}
	}
}

void DesignExtractor::insertCallsData(vector<PROC_INDEX> callStmt) {
	//--Loop which inserts data for all the call statements in the program--
	for(int i =0;i<callStmt.size();i++) {
		astquery->gotoStmtAt(callStmt.at(i));
		PROC_INDEX p = astquery->getCallProcID();
		vector<PROC_INDEX> procList = callsTable->getList(p);
		vector<VAR_INDEX> varMod,varUse;
		//--Loop to go through the list of procedure modified/used in calls--
		for(int j=0;j<procList.size();j++) {
			vector<VAR_INDEX> temp1 = modifyProc->getVarModified(procList.at(j));
			vector<VAR_INDEX> temp2 = usesProc->getVarUsed(procList.at(j));
			//------------ For variables Modified ------------
			//-Check if its already there, if not add to List-
			for(int k=0;k<temp1.size();k++) {
				bool temp = true;
				for(int l=0;l<varMod.size();l++) {
					if(temp1.at(k)==varMod.at(l)) {
						temp = false;
					}
				}
				if(temp == true) {
					varMod.push_back(temp1.at(k));
				}
			}
			//-------------- For variables Used --------------
			//-Check if its already there, if not add to List-
			for(int k=0;k<temp2.size();k++) {
				bool temp = true;
				for(int l=0;l<varUse.size();l++) {
					if(temp2.at(k)==varUse.at(l)) {
						temp = false;
					}
				}
				if(temp == true) {
					varUse.push_back(temp2.at(k));
				}
			}
		}

		//--Getting the list of all statements to which change has to be made--
		vector<STATEMENT_INDEX> stmts;
		stmts.push_back(astquery->getCurrStmtIndex());
		while(astquery->getLinksId()[stmtNode::PARENT] != -1) {
				astquery->gotoStmtAt(astquery->getLinksId()[stmtNode::PARENT]);
				stmts.push_back(astquery->getCurrStmtIndex());
		}
		//-------------------adding to modifyVar/useVar-----------------------
		
		for(int j=0;j<varMod.size();j++) {
			modifyVar->addProc(varMod.at(j),procTable->getProcIn(stmts.at(0)));
			for(int i=0;i<stmts.size();i++) {
				modifyVar->addStmt(varMod.at(j),stmts.at(i));
			}
		}
		for(int j=0;j<varUse.size();j++) {
			usesVar->addProc(varUse.at(j),procTable->getProcIn(stmts.at(0)));
			for(int i=0;i<stmts.size();i++) {
				usesVar->addStmt(varUse.at(j),stmts.at(i));
			}
		}
		//--------------------adding to modifyStat/usesStat--------------------

		for(int i=0;i<stmts.size();i++) {
			modifyStat->addEntry(stmts.at(i),varMod);
			usesStat->addEntry(stmts.at(i),varUse);
		}

		//-------------------adding to modifyProc/usesProc---------------------

		modifyProc->addEntry(procTable->getProcIn(stmts.at(0)),varMod);
		usesProc->addEntry(procTable->getProcIn(stmts.at(0)),varUse);
	}
}

void DesignExtractor::getNextStmts(STMT_VCTR &stmtVector) {
	if(astquery->getLinksId()[stmtNode::NEXT_STMT] != stmtNode::INVALID_INDEX) {
		stmtVector.push_back(astquery->getLinksId()[stmtNode::NEXT_STMT]);
	}
	if(astquery->getLinksId()[stmtNode::NEXT_TO_ELSE] != stmtNode::INVALID_INDEX) {
		stmtVector.push_back(astquery->getLinksId()[stmtNode::NEXT_TO_ELSE]);
	}
	if(astquery->getLinksId()[stmtNode::NEXT_TO_THEN] != stmtNode::INVALID_INDEX) {
		stmtVector.push_back(astquery->getLinksId()[stmtNode::NEXT_TO_THEN]);
	}
	if(astquery->getLinksId()[stmtNode::NEXT_TO_WHILE] != stmtNode::INVALID_INDEX) {
		stmtVector.push_back(astquery->getLinksId()[stmtNode::NEXT_TO_WHILE]);
	}
}